import { io, Socket, type ManagerOptions, type SocketOptions } from 'socket.io-client'
import * as Mediasoup from 'mediasoup-client'
import { types } from 'mediasoup-client'
import * as MediaClientTypes from './Types'
import Logger from './Logger'
import Dispense from './dispense'
interface mediaClientOption {
    socketio?: Partial<ManagerOptions & SocketOptions>
}
export default class MediaClient extends Dispense {
    room: string = ''
    io!: Socket
    log!: Logger
    device: types.Device | null = null

    tracks: MediaStreamTrack[] = []

    sendTransport: types.Transport | null = null
    recTransport: types.Transport | null = null

    producers = new Map<string, types.Producer>()
    consumers = new Map<string, types.Consumer>()

    micProducer: types.Producer | null = null
    videoProducer: types.Producer | null = null

    micClosed = true
    videoClosed = true
    //这个客户端是否消费
    consume = true
    options: mediaClientOption = {}
    constructor(socketUrl: string = '', options: mediaClientOption = {}) {
        super()
        // this.room = ''
        this.log = new Logger('tlf-MediasoupClient')
        this.log.debug('初始化')
        const url = socketUrl
        this.options = options
        this.initIo(url)
    }

    initIo(url: string) {
        this.io = io(url, this.options.socketio || {});
        this.io.on('connectSuccess', e => {
            this.log.debug('连接成功 id:', e)
        })
        this.io.on('connect_error', e => {
            this.log.debug('连接失败', e)
        })
        this.io.on('disconnect', (e: string) => {
            this.log.debug('断开连接', e)
            if (e == 'transport close') {
                window.location.reload()
            }
        })
        this.io.on('otherClientCreateProducer', async ({ socketId, producerId }: { socketId: string, producerId: string }) => {
            this.log.debug('其他客户端上线主动推送过来的数据', producerId)
            if (this.consume) {
                let consumer = await this.createConsumer(producerId)
                await this.resumerConsumer(consumer)
                this.trigger('newConsumer', socketId, producerId, consumer)
            }
        })
        this.io.on('producerClosed', ({ producers }: { producers: string[] }) => {
            this.log.debug('收到其他客户端producerClosed的消息')
            this.log.debug(producers)
            producers.forEach(v => {

                const con = this.consumers.get(v)
                if (con) {
                    this.closeConsumer(con)
                }
                // con?.close()
                // this.consumers.delete(v)
                // this.trigger('closeConsumer', con)
            })
        })

        this.io.on('userJoinRoom', data => {
            this.log.debug('收到其他客户端加入房间的消息')
            this.log.debug(data)
            this.trigger('userJoinRoom', data)
        })
    }
    getRoomStatus(): Promise<MediaClientTypes.RoomStatus> {
        return new Promise((reslove, reject) => {
            this.io.emit('getRoomStatus', (data: MediaClientTypes.RoomStatus) => {
                reslove(data)
            })
        })
    }
    join(room: string, name: string, transport = true) {
        return new Promise((reslove, inject) => {
            if (this.room || room === '') {
                this.log.warn('已经加入过了')
                inject()
                return
            }
            this.io.emit('joinRoom', { room: room, appdata: { name } }, async (data: MediaClientTypes.joinRoomCallbackData) => {
                this.log.debug('joinRoom 返回的数据', data)
                this.room = room
                if (transport) {
                    await this.createDevice(data.rtpCapabilities)
                    await this.createSendTransport()
                    await this.createRecTransport()
                }
                await this.getRemoteProducer()
                this.d('*****************加入房间初始化完成*****************')
                reslove(this)
            })
        })
    }
    leave() {
        if (!this.room) {
            return
        }

        if (this.sendTransport) {
            this.sendTransport.close()
            this.io.emit('transportClosed', { transportId: this.sendTransport.id })
            this.producers.forEach(v => {
                v.close()
            })
            this.sendTransport = null
            this.producers = new Map()

            this.micProducer = null
            this.micClosed = true

            this.videoProducer = null
            this.videoClosed = true
        }
        if (this.recTransport) {
            this.recTransport.close()
            this.io.emit('transportClosed', { transportId: this.recTransport.id })
            this.recTransport = null
            this.consumers = new Map()
            // TODO
        }

        this.device = null
        this.room = ''
        this.io.emit('leaveRoom', { room: this.room })
    }
    async createDevice(rtpCapabilities: types.RtpCapabilities) {
        if (this.device) {
            this.log.warn('device 已经创建了')
            return false
        }
        this.device = new Mediasoup.Device()
        await this.device.load({
            routerRtpCapabilities: rtpCapabilities
        })
    }
    async createSendTransport() {
        return new Promise((r, i) => {
            this.log.debug('准备创建sendTransport')
            this.io.emit('createSendWebrtcTransport', (data: MediaClientTypes.createWebrtcTransportCallbackData) => {
                const { params, error } = data
                if (error) {
                    this.log.error('创建sendTransport失败:', error)
                    i(error)
                    return
                }
                if (!this.device) {
                    this.d('device 没创建')
                    i()
                    return
                }
                this.log.debug('服务器webRtcTransport参数', params)
                this.sendTransport = this.device.createSendTransport({
                    id: params.id,
                    iceParameters: params.iceParameters,
                    iceCandidates: params.iceCandidates,
                    dtlsParameters: params.dtlsParameters
                })
                this.log.debug('sendTransport创建成功', this.sendTransport.id)
                this.log.debug("准备给sendTansport绑定事件")
                this.sendTransport.on('connect', async (data, callback) => {
                    this.log.debug('sendTransport 触发 connect', data)
                    this.io.emit('sendTransportConnect', {
                        dtlsParameters: data.dtlsParameters
                    })
                    callback()
                })
                this.sendTransport.on('produce', async (data, callback) => {
                    this.log.debug('sendTransport 触发 produce', data)
                    this.io.emit('sendTransportProduce', data, (resData: MediaClientTypes.sendTransportProduceCallbackData) => {
                        this.log.debug('服务器发来producer的id', resData)
                        callback({
                            id: resData.id
                        })
                        this.log.info('producer 开始上传', resData.id)
                    })
                })
                this.log.debug("给sendTansport绑定事件完成")
                r(this.sendTransport)
            })
        })

    }
    async createRecTransport() {
        return new Promise((r, i) => {
            this.log.debug('准备创建recTransport')
            this.io.emit('createRecWebrtcTransport', (data: MediaClientTypes.createWebrtcTransportCallbackData) => {
                const { params, error } = data
                if (error) {
                    this.log.error('创建recTransport失败', error)
                    i(error)
                    return
                }
                if (!this.device) {
                    this.d('device 没创建')
                    i()
                    return
                }
                this.log.debug('创建recTransport 服务端参数', data.params)
                this.recTransport = this.device.createRecvTransport({
                    id: params.id,
                    iceCandidates: params.iceCandidates,
                    iceParameters: params.iceParameters,
                    dtlsParameters: params.dtlsParameters
                })
                this.log.debug('recTransport创建成功', this.recTransport.id)

                this.log.debug('准备给 recTransport 绑定事件成功')

                this.recTransport.on('connect', async (data, callback) => {
                    this.log.debug('recTransport 触发connect 事件', data)
                    this.io.emit('recTransportConnect', {
                        dtlsParameters: data.dtlsParameters
                    })
                    callback()
                })
                this.log.debug('recTransport 绑定事件成功')
                r(this.recTransport)
            })
        })

    }
    async createPlainTransport(): Promise<MediaClientTypes.createPlainTransport> {
        return new Promise((r, i) => {
            this.io.emit('createPlainTransport', (data: Promise<MediaClientTypes.createPlainTransport>) => {
                this.d("createPlainTransport 成功")
                this.d(data)
                r(data)
            })
        })
    }
    async createProducer() {
        await this.enableMicProducer()
        await this.enableVideoProducer()
    }

    async enableMicProducer() {
        this.d('启用mic producer')
        const track = this.tracks.find(v => {
            return v.kind === 'audio'
        })
        if (!this.sendTransport) {
            await this.createSendTransport()
        }
        if (this.sendTransport && track) {
            if (this.micProducer) {
                this.micProducer.replaceTrack({ track })
            } else {
                this.micProducer = await this.sendTransport.produce({ track })
                this.micProducer.on('trackended', () => {
                    this.d("mic producer trackended")
                    this.disableMicProducer()
                })
                this.micProducer.on("transportclose", () => {
                    this.d('mic producer transportClosed')
                    this.micProducer = null
                })
            }
            this.producers.set(this.micProducer.id, this.micProducer)
            this.micClosed = false
            this.d('启用mic producer 成功')
        } else {
            this.d('启用mic producer 失败')
        }
    }
    async disableMicProducer() {
        this.d('禁用mic producer')
        if (!this.micProducer) {
            return
        }
        this.micProducer.close()
        this.io.emit('producerClosed', { producerId: this.micProducer.id })
        this.micClosed = true
        this.micProducer = null
    }

    async muteMic() {
        this.d('静音')
        if (!this.micProducer) {
            return
        }
        this.micProducer.pause()
        this.io.emit('producerPause', { producerId: this.micProducer.id })
    }
    async unmuteMic() {
        this.d('取消静音')
        if (!this.micProducer) {
            return
        }
        this.micProducer.resume()
        this.io.emit('producerResume', { producerId: this.micProducer.id })
    }

    async enableVideoProducer() {
        this.d('启用video producer')
        const track = this.tracks.find(v => {
            return v.kind === 'video'
        })
        if (!this.sendTransport) {
            await this.createSendTransport()
        }
        if (this.sendTransport && track) {
            if (this.videoProducer) {
                this.videoProducer.replaceTrack({ track })
            } else {
                this.videoProducer = await this.sendTransport.produce({ track })
                this.videoProducer.on('trackended', () => {
                    this.disablevideoProducer()
                })
                this.videoProducer.on("transportclose", () => {
                    this.videoProducer = null
                })
            }
            this.producers.set(this.videoProducer.id, this.videoProducer)
            this.micClosed = false
            this.d('启用video producer 成功')
        } else {
            this.d('启用video producer 失败')
        }
    }
    async disablevideoProducer() {
        this.d('禁用video producer')
        if (!this.videoProducer) {
            return
        }
        this.videoProducer.close()
        this.io.emit('producerClosed', { producerId: this.videoProducer.id })
        this.micClosed = true
        this.videoProducer = null
    }

    async setStream(stream: MediaStream) {
        this.log.debug('设置视频流')
        const tracks = stream.getTracks()
        this.tracks = tracks
        this.log.debug(tracks)
    }
    /**
     * 获取远程的producer,如果服务器有producer这个时间会触发otherClientCreateProducer事件自动创建consumer
     * @returns 
     */
    async getRemoteProducer(): Promise<MediaClientTypes.remoteProducerState[]> {
        return new Promise((r, i) => {
            this.io.emit('getProducerState', async (data: { producers: MediaClientTypes.remoteProducerState[], sockets: string[] }) => {
                this.log.debug('获取其他客户端的producer', data)
                data.producers.forEach(async (v) => {
                    // await this.createConsumer(v)
                })
                r(data.producers)
            })
        })
    }

    async createConsumer(producerId: string): Promise<types.Consumer> {
        const producer = {
            producerId
        }
        return new Promise(async (r, i) => {
            if (!this.recTransport) {
                await this.createRecTransport()
            }
            if (this.consumers.get(producer.producerId)) {
                this.log.warn('这个producer已经创建过消费者了')
                i(false)
                return
            }
            if (!this.device) {
                i(false)
                return
            }
            //临时占个位置,为了确保不会因为网络问题导致创建多个consumer
            // 忽略ts检查
            // @ts-ignore
            this.consumers.set(producer.producerId, true)

            this.io.emit('createConsumer', {
                producerId: producer.producerId,
                rtpCapabilities: this.device.rtpCapabilities
            }, async (data: MediaClientTypes.createConsumerCallbackData) => {
                this.log.debug('创建消费者服务端返回的参数', data)
                if (!this.recTransport) {
                    this.d('recTransport 没有')
                    return
                }
                const consumer = await this.recTransport.consume({
                    id: data.consumerId,
                    producerId: data.producerId,
                    kind: data.kind,
                    rtpParameters: data.rtpParameters,
                })
                consumer.on('transportclose', () => {
                    this.d('consumer transportclosed', consumer.id)
                    consumer.close()
                    this.consumers.delete(producer.producerId);
                });
                consumer.on('trackended', () => {
                    this.d('consumer trackended')
                    consumer.close()
                    this.consumers.delete(producer.producerId);
                    this.trigger('closeConsumer', consumer)
                })
                // consumer.appData.socketId = producer.socketId
                this.consumers.set(producer.producerId, consumer)
                // consumer.on('')
                this.log.debug('创建消费者成功', consumer.id)
                r(consumer)
            })
        })
    }

    async resumerConsumer(consumer: types.Consumer) {
        return new Promise((r, i) => {
            this.log.debug('重新播放', consumer.id)

            this.io.emit('consumerResume', { consumerId: consumer.id }, (data: boolean) => {
                this.log.debug('resume', data)
                consumer.resume()
                // consumer.track.enabled = true
                r(data)
            })
        })

    }
    async pauseConsumer(consumer: types.Consumer) {
        return new Promise((r, i) => {
            this.log.debug("暂停播放", consumer.id)
            this.io.emit('consumerPause', ({ consumerId: consumer.id }), (data: any) => {
                consumer.pause()
                // consumer.track.enabled = false
                this.log.debug('暂停播放', data)
                r(data)
            })
        })

    }
    async closeConsumer(consumer: types.Consumer) {
        return new Promise((r, i) => {
            this.log.debug("closeConsumer", consumer.id)
            this.io.emit('closeConsumer', ({ consumerId: consumer.id }), (data: any) => {
                consumer.close()
                this.log.debug('closeConsumer', data)
                this.consumers.delete(consumer.producerId)
                this.trigger('closeConsumer', consumer)
                r(data)
            })
        })

    }
    async closeProducer(producer: types.Producer) {
        return new Promise((r, i) => {
            this.log.debug("closeProducer", producer.id)
            this.io.emit('closeProducer', ({ producerId: producer.id }), (data: any) => {
                producer.close()
                this.producers.delete(producer.id)
                this.log.debug('closeProducer', data)
                r(data)
            })
        })
    }
    // getConsumers() {
    //     const ret = [] as Array<MediaClientTypes.getResumersData>
    //     this.consumers.forEach((consumer, producerId) => {
    //         const tmp = {
    //             producerId: producerId,
    //             consumer: consumer,
    //             track: new MediaStream([consumer.track]),
    //             kind: consumer.kind
    //         }
    //         ret.push(tmp)
    //     })
    //     this.log.debug('获取消费者', ret)
    //     return ret
    // }
    async close() {
        this.io.close()
    }
    d(f: any, ...arg: any[]) {
        this.log.debug(f, ...arg)
    }
}
